package ltd.nullpointer.tcp.server.conf;

import com.boot2.core.exception.BusinessException;
import com.boot2.core.utils.ReflectUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import javassist.CannotCompileException;
import javassist.NotFoundException;
import ltd.nullpointer.tcp.core.MessageConfig;
import ltd.nullpointer.tcp.core.MessageRouter;
import ltd.nullpointer.tcp.core.annotation.TcpDownMessage;
import ltd.nullpointer.tcp.core.annotation.TcpDownOffset;
import ltd.nullpointer.tcp.core.annotation.TcpUpMessage;
import ltd.nullpointer.tcp.core.annotation.TcpUpOffset;
import ltd.nullpointer.tcp.core.message.NPiotTCPMessage;
import ltd.nullpointer.tcp.core.message.TcpMessageAdaptor;
import ltd.nullpointer.tcp.core.resolver.AbstractTCPMessageClassFieldEntry.ClassFieldEntry;
import ltd.nullpointer.tcp.core.resolver.AbstractTCPMessageResolver;
import ltd.nullpointer.tcp.core.resolver.MessageResolverFactory;
import ltd.nullpointer.tcp.core.resolver.NPiotTCPMessageResolver;
import ltd.nullpointer.tcp.core.resolver.parser.ResolverParser;
import ltd.nullpointer.tcp.core.serialize.AbstractTCPMessageSerializer;
import ltd.nullpointer.tcp.core.serialize.MessageSerializerFactory;
import ltd.nullpointer.tcp.core.serialize.NPiotTCPMessageSerializer;
import ltd.nullpointer.tcp.core.serialize.parser.SerializerParser;
import ltd.nullpointer.tcp.server.netty.NpChannelInitializer;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.type.filter.AnnotationTypeFilter;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;

@Configuration
// @EnableAutoConfiguration
public class NettyConfig {

    @Autowired(required = false)
    NPiotTCPMessageResolver npiotTCPMessageResolver;

    @Autowired(required = false)
    List<AbstractTCPMessageResolver> abstractTCPMessageResolverList;

    @Autowired
    AbstractTCPMessageSerializer tcpMessageSerializer;

    @Autowired(required = false)
    NPiotTCPMessageSerializer npiotTCPMessageSerializer;

    @Autowired
    List<ResolverParser<?>> resolverParserList;

    @Autowired
    List<SerializerParser<?>> serializerParserList;

    @Autowired
    MessageConfig messageConfig;

    @Autowired(required = false)
    private List<MessageRouter> messageRouterList;

    @Autowired
    private NpChannelInitializer npChannelInitializer;

    @Resource(name = "bossGroup")
    NioEventLoopGroup bossGroup;

    @Resource(name = "workerGroup")
    NioEventLoopGroup workerGroup;

    @Resource(name = "tcpChannelOptions")
    Map<ChannelOption<?>, Object> tcpChannelOptions;

    @PostConstruct
    public void init() throws CannotCompileException, InstantiationException, NotFoundException, IllegalAccessException {

        Map<String, ClassFieldEntry> classMessageUpFieldEntryMap = initAndCacheMessageUpClassFieldEntryMap();
        Map<String, ClassFieldEntry> classMessageDownFieldEntryMap = initAndCacheMessageDownClassFieldEntryMap();

        if (null != npiotTCPMessageResolver) {
//            npiotTCPMessageResolver.setClassFieldEntryMap(classMessageUpFieldEntryMap);
            // 向sdk中注入业务报文解析器,实现自定义业务报文解析，客户端可以以同样方式注入自己的解析器
            MessageResolverFactory.getInstance().bindMessageAnalyzer(NPiotTCPMessage.ARK_HEADER_BYTE, npiotTCPMessageResolver);
        }

        if (null != npiotTCPMessageSerializer) {
//            npiotTCPMessageSerializer.setClassFieldEntryMap(classMessageUpFieldEntryMap);
            MessageSerializerFactory.getInstance().bindMessageAnalyzer(NPiotTCPMessage.class, npiotTCPMessageSerializer);
        }
        MessageSerializerFactory.getInstance().bindMessageAnalyzer(TcpMessageAdaptor.class, tcpMessageSerializer);

        Map<Class<?>, ResolverParser<?>> resolverParserMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(resolverParserList)) {
            resolverParserList.forEach(e -> {
                resolverParserMap.put(e.getClass(), e);
            });
        }

        Map<Class<?>, SerializerParser<?>> serializerParserMap = new HashMap<>();
        if (CollectionUtils.isNotEmpty(serializerParserList)) {
            serializerParserList.forEach(e -> {
                serializerParserMap.put(e.getClass(), e);
            });
        }

        if (CollectionUtils.isNotEmpty(abstractTCPMessageResolverList)) {
            abstractTCPMessageResolverList.forEach(e -> {
                e.setClassFieldEntryMap(classMessageUpFieldEntryMap);
                e.regResolverParserMap(resolverParserMap);
            });
        }

        MessageResolverFactory.regResolvers(messageConfig.getHeaderByteArr(), classMessageUpFieldEntryMap, resolverParserMap);
        tcpMessageSerializer.regSerializerParserMap(serializerParserMap);
        tcpMessageSerializer.setClassFieldEntryMap(classMessageDownFieldEntryMap);

        if (null != messageRouterList) {
            messageRouterList.forEach(e -> e.init());
        }
    }

    /**
     * 最前置的解码器，一般可追加拆包解码 器
     * @return
     */
//    @Bean
//    @Qualifier("beforeByteToMessageDecoderList")
//    @Scope("prototype")
//    public List<ByteToMessageDecoder> beforeByteToMessageDecoderList() {
//        List<ByteToMessageDecoder> byteToMessageDecoderList = new ArrayList<>(1);
//        LengthFieldBasedFrameDecoder lengthFieldBasedFrameDecoder = new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 3, 2, 2, 0);
//        byteToMessageDecoderList.add(lengthFieldBasedFrameDecoder);
//        return byteToMessageDecoderList;
//    }

    private Map<String, ClassFieldEntry> initAndCacheMessageUpClassFieldEntryMap() {
        Set<BeanDefinition> components = scanComponents(TcpUpMessage.class);
        Map<String, ClassFieldEntry> classFieldEntryMap = new HashMap<>();
        for (BeanDefinition component : components) {
            String className = component.getBeanClassName();
            System.out.printf("上行消息 Component: %s\n", className);
            Class<?> clazz;
            try {
                clazz = Class.forName(className);
                TcpUpMessage tcpMessage = clazz.getAnnotation(TcpUpMessage.class);
                List<Field> fieldList = ReflectUtil.getAllFieldListWithParentAndAnnotation(clazz, TcpUpOffset.class);
                String[] msgCodeArr = tcpMessage.messageCode();
                ClassFieldEntry classFieldEntry = new ClassFieldEntry();
                classFieldEntry.setClazz(clazz);

                //需要对属性，根据解析顺序重排序
                Collections.sort(fieldList, (e1, e2) -> {
                    TcpUpOffset tcpOffset1 = e1.getAnnotation(TcpUpOffset.class);
                    TcpUpOffset tcpOffset2 = e2.getAnnotation(TcpUpOffset.class);
                    int start1 = tcpOffset1.start();
                    int start2 = tcpOffset2.start();

                    if (start1 > start2) {
                        return 1;
                    } else if (start1 < start2) {
                        return -1;
                    }
                    return 0;
                });
                classFieldEntry.setFieldList(fieldList);
                for (String msgCode : msgCodeArr) {
                    classFieldEntryMap.put(msgCode, classFieldEntry);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                throw new BusinessException("扫描包 初始化失败...", e);
            }
        }
        return classFieldEntryMap;
    }

    /**
     * 初始化下行消息
     *
     * @return
     */
    private Map<String, ClassFieldEntry> initAndCacheMessageDownClassFieldEntryMap() {
        Set<BeanDefinition> components = scanComponents(TcpDownMessage.class);
        Map<String, ClassFieldEntry> classFieldEntryMap = new HashMap<>();
        for (BeanDefinition component : components) {
            String className = component.getBeanClassName();
            System.out.printf("下行消息 Component: %s\n", className);
            Class<?> clazz;
            try {
                clazz = Class.forName(className);
                TcpDownMessage tcpMessage = clazz.getAnnotation(TcpDownMessage.class);
                List<Field> fieldList = ReflectUtil.getAllFieldListWithParentAndAnnotation(clazz, TcpDownOffset.class);
                String headerHexStr = tcpMessage.header().value();
                String typeHexStr = tcpMessage.type().value();
                String msgCode = headerHexStr + typeHexStr;
                ClassFieldEntry classFieldEntry = new ClassFieldEntry();
                classFieldEntry.setClazz(clazz);

                //需要对属性，根据解析顺序重排序
                Collections.sort(fieldList, (e1, e2) -> {
                    TcpDownOffset tcpOffset1 = e1.getAnnotation(TcpDownOffset.class);
                    TcpDownOffset tcpOffset2 = e2.getAnnotation(TcpDownOffset.class);

                    if (null == tcpOffset1 || null == tcpOffset2) {
                        return 0;
                    }
                    int start1 = tcpOffset1.start();
                    int start2 = tcpOffset2.start();

                    if (start1 > start2) {
                        return 1;
                    } else if (start1 < start2) {
                        return -1;
                    }
                    return 0;
                });
                classFieldEntry.setFieldList(fieldList);
                classFieldEntryMap.put(msgCode, classFieldEntry);

            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                throw new BusinessException("扫描包 初始化失败...", e);
            }
        }
        return classFieldEntryMap;
    }

    private Set<BeanDefinition> scanComponents(Class<? extends Annotation> annotationType) {
        ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
        // provider.addIncludeFilter(new AnnotationTypeFilter(Component.class));
        // 查找注解了Table的所有类
        provider.addIncludeFilter(new AnnotationTypeFilter(annotationType));
        //todo 改成从spring上下文获取
        String basePackage = "ltd.nullpointer.tcp.core.message";
        Set<BeanDefinition> components = provider.findCandidateComponents(basePackage);
        basePackage = "ltd.nullpointer.frontend.model";
        Set<BeanDefinition> components2 = provider.findCandidateComponents(basePackage);
        components.addAll(components2);
        return components;
    }

    @SuppressWarnings("unchecked")
    @Bean(name = "serverBootstrap")
    public ServerBootstrap bootstrap() {
        ServerBootstrap b = new ServerBootstrap();
        b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.DEBUG))
                .childHandler(npChannelInitializer);
        if (tcpChannelOptions!=null) {
            Set<ChannelOption<?>> keySet = tcpChannelOptions.keySet();
            for (@SuppressWarnings("rawtypes")
                    ChannelOption option : keySet) {
                b.option(option, tcpChannelOptions.get(option));
            }
        }
        return b;
    }


//    @Bean(name = "tcpSocketAddress")
//    public InetSocketAddress tcpPort() {
//        return new InetSocketAddress(tcpProperties.getPort());
//    }
}